/*
 * BLinkStreet.java
 * 
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.core.basenet;

import org.fpdev.core.transit.TransitPath;
import org.fpdev.util.FPUtil;

import java.awt.*;
import java.util.Iterator;
import org.fpdev.core.Scenario;
import org.fpdev.apps.rtemaster.gui.map.MapCanvas;

public class BLinkStreet extends BLink {

  private int parentID_;
  private int fAddrR_,  fAddrL_,  tAddrR_,  tAddrL_,  zipL_,  zipR_;
  private int transitProv_,  bikePedProv_;
  private int type_;

  //private Map<Short, Short> tProvs_, bProvs_;

  public BLinkStreet(Scenario s, int id, int type, BNode fNode, BNode tNode, String dispName, int lenFt) {
    super(s, id, fNode, tNode, dispName, lenFt);
    fAddrR_ = fAddrL_ = tAddrR_ = tAddrL_ = zipL_ = zipR_ = 0;
    transitProv_ = bikePedProv_ = 0;
    parentID_ = (id < 1000000 ? id : 0);
    type_ = type;

    //tProvs_ = new HashMap<Short, Short>();
    //bProvs_ = new HashMap<Short, Short>();
  }

  public BLinkStreet(BLink link, int type) {
    this(link.scenario_, link.id_, type, link.fNode_, link.tNode_, link.displayName_, link.getLengthFeet());
  }

  public void setParentID(int pID) {
    parentID_ = pID;
  }

  public int getParentID() {
    return parentID_;
  }

  public void setTransitProv(int p) {
    transitProv_ = p;
  }

  public int getTransitProv() {
    return transitProv_;
  }

  public void setBikePedProv(int p) {
    bikePedProv_ = p;
  }

  public int getBikePedProv() {
    return bikePedProv_;
  }

  @Override
  public void setType(int type) {
    if (type >= 10 && type < 50) {
      type_ = type;
    } else {
    // TODO throw exception for invalid?      
    }
  }

  public void setTypePreserveDirection(int type) {
    if (type >= 10 && type < 50) {
      if (type_ % 10 == 1) { // one way types
        type_ = type - (type % 10) + 1;
      } else {
        type_ = type;
      }
    } else {
    // TODO throw exception for invalid?      
    }
  }

  @Override
  public int getType() {
    return type_;
  }

  @Override
  public int getExtendedType() {
    return type_*100 + transitProv_*10 + bikePedProv_;
  }

  @Override
  public int getClassType() {
    return BLink.CLASS_STREET;
  }

  private int getBaseWidth(double res, int type, int transitProv, int bikePedProv) {

    int trn = transitProv > 0 ? 2 : 0;
    if (res < 7.5) {
      switch (type / 10) {
        case LinkTypes.MAJOR_TFARE:
          return trn + 10;
        case LinkTypes.MINOR_TFARE:
          return trn + 9;
        case LinkTypes.NONWALKABLE:
          return trn + 5;
        default:
          return trn + 7;
      }
    }
    if (res < 15) {
      switch (type / 10) {
        case LinkTypes.MAJOR_TFARE:
          return trn + 8;
        case LinkTypes.MINOR_TFARE:
          return trn + 7;
        case LinkTypes.NONWALKABLE:
          return trn + 5;
        default:
          return trn + 6;
      }
    }
    if (res < 40) {
      switch (type / 10) {
        case LinkTypes.MAJOR_TFARE:
          return trn + 7;
        case LinkTypes.MINOR_TFARE:
          return trn + 6;
        case LinkTypes.NONWALKABLE:
          return trn + 5;
        default:
          return trn + 5;
      }
    }
    if (res < 70) {
      switch (type / 10) {
        case LinkTypes.MAJOR_TFARE:
          return trn + 5;
        case LinkTypes.MINOR_TFARE:
          return trn + 4;
        //case LinkTypes.NONWALKABLE: return 1;
        default:
          return trn + 1;
      }
    }
    if (res < 100) {
      return (type / 10 == LinkTypes.MAJOR_TFARE) ? 2 : 1;
    }
    return (type ==  0) ? 0 : 1;
  }

  @Override
  public void drawBG(MapCanvas canvas, Scenario scen) {
    double res = canvas.getCC().getResolution();

    int type, transitProv, bikePedProv;
    if(typeOverride_ && scenID_ != scen.getID()) {
      int fullType = canvas.getBaseNet().getOverriddenFullType(this, scen);
      type = fullType/100;
      transitProv = fullType/10 % 10;
      bikePedProv = fullType % 10;
    }
    else {
      type = type_;
      transitProv = transitProv_;
      bikePedProv = bikePedProv_;
    }
    if(type == 0) return;

    int thickness = getBaseWidth(res, type, transitProv, bikePedProv);

    /*if(zipL_ == 0 || zipR_ == 0) {
    canvas.setColor(Color.GREEN);
    canvas.setLineWidth(thickness+6);
    super.drawCurve(canvas);
    }*/
    //bikepedProv = typeOverride_ ?

    if (bikePedProv > 0) {
      Color bpColor = Color.GREEN;
      int dash1 = 1, dash2 = 2;

      switch (bikePedProv) {
        case LinkTypes.BPPROV_BARRIER_SEPARATED_MULTIUSE:
        case LinkTypes.BPPROV_BARRIER_SEPARATED_BIKE:
          bpColor = new Color(0, 127, 0);
          dash1 = 3;
          break;
        case LinkTypes.BPPROV_STRIPED_LANES_FULL:
          bpColor = new Color(0, 127, 0);
          break;
        case LinkTypes.BPPROV_BIKEABLE_SHOULDER:
          dash1 = 2;
      }

      canvas.setColor(bpColor);
      float[] dash = {dash1, dash2};
      canvas.setStroke(new BasicStroke(thickness + 6, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, dash, 0));
      super.drawCurve(canvas);

      canvas.setColor(Color.WHITE);
      //canvas.setLineWidth(thickness+3);
      canvas.setStroke(new BasicStroke(thickness + 3, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
      super.drawCurve(canvas);
    }

    // main color stroke
    Color mainColor = Color.BLACK;
    //thickness = getBaseWidth(canvas.getCC().getResolution());

    switch (type / 10) {
      case LinkTypes.MAJOR_TFARE:
        mainColor = new Color(127, 0, 0);
        break;
      case LinkTypes.MINOR_TFARE:
        mainColor = new Color(0, 0, 255);
        break;
      case LinkTypes.OTHER_STREET:
        mainColor = res < 40 ? Color.darkGray : Color.lightGray;
        break;
      case LinkTypes.NONWALKABLE:
        mainColor = Color.lightGray;
        break;
    }

    canvas.setColor(mainColor);
    //canvas.setLineWidth(thickness);
    canvas.setStroke(new BasicStroke(thickness, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
    super.drawCurve(canvas);//, isOneWay() ? 1 : 0, 5);

  }

  @Override
  @SuppressWarnings("empty-statement")
  public void drawFG(MapCanvas canvas, Scenario scen) {
    //if(type_/10 == LinkTypes.NONWALKABLE) return;
    Color color = Color.white;
    double res = canvas.getCC().getResolution();
    
    int type, transitProv, bikePedProv;
    if(typeOverride_ && scenID_ != scen.getID()) {
      int fullType = canvas.getBaseNet().getOverriddenFullType(this, scen);
      //System.out.println("fg, link w/ overridden type, id="+id_+", new type="+fullType);
      type = fullType/100;
      transitProv = fullType/10 % 10;
      bikePedProv = fullType % 10;
    }
    else {
      type = type_;
      transitProv = transitProv_;
      bikePedProv = bikePedProv_;
    }
    if(type == 0) return;
        
    int thickness = getBaseWidth(res, type, transitProv, bikePedProv) - (res < 15 ? 3 : 2);
    if (thickness <= 0) {
      return;
    }

    switch (type / 10) {
      case LinkTypes.MAJOR_TFARE:
        color = new Color(255, 255, 180);
        break;
      case LinkTypes.MINOR_TFARE:
        color = new Color(200, 255, 255);
        break;
      case LinkTypes.NONWALKABLE:
        switch(type) {
          case LinkTypes.NW_EXPWY_GENERAL:
          case LinkTypes.NW_EXPWY_RAMP:
            color = Color.white;
            break;
          default: color = Color.lightGray;
        }
        break;
    }
    if (res < 70) {
      if (res < 40 || type / 10 != LinkTypes.OTHER_STREET) {
        canvas.setColor(color);

        if(type == LinkTypes.NW_EXPWY_RAMP) {
          float[] dash = {4, 3};
          canvas.setStroke(new BasicStroke(thickness, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, dash, 0));
        }
        else 
          canvas.setStroke(new BasicStroke(thickness, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
        super.drawCurve(canvas);//, isOneWay() ? 1 : 0, 5);

        if(transitProv == LinkTypes.TRPROV_BARRIER_SEPARATED_LRT ||
           transitProv == LinkTypes.TRPROV_INTEGRATED_LRT) {
          canvas.setColor(new Color(0, 128, 0));
          canvas.setStroke(new BasicStroke(3));
          super.drawCurve(canvas);
          canvas.setColor(Color.white);
          float[] dash = {3, 3};
          canvas.setStroke(new BasicStroke((transitProv == LinkTypes.TRPROV_BARRIER_SEPARATED_LRT ? 1 : 2), BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, dash, 0));
          super.drawCurve(canvas);
        }
        if (isOneWay() && type/10 != LinkTypes.NONWALKABLE) {
          canvas.drawArrow(this, 4, false, Color.lightGray);
        }
      }
    }

  }

  @Override
  public int getDrawWidth(MapCanvas canvas, Scenario scen) {
    int type, transitProv, bikePedProv;
    if(typeOverride_ && scenID_ != scen.getID()) {
      int fullType = canvas.getBaseNet().getOverriddenFullType(this, scen);
      type = fullType/100;
      transitProv = fullType/10 % 10;
      bikePedProv = fullType % 10;
    }
    else {
      type = type_;
      transitProv = transitProv_;
      bikePedProv = bikePedProv_;
    }
    int w = getBaseWidth(canvas.getCC().getResolution(), type, transitProv, bikePedProv);
    if (bikePedProv_ > 0) {
      w += 6;
    }
    return w;
  }

  @Override
  public String getMoreXML() {
    String ret = "";
    //ret = ret + " walkable=\""+walkable_+"\"";
    //ret = ret + " major=\""+major_+"\"";
    ret = ret + " faddrl=\"" + fAddrL_ + "\"";
    ret = ret + " faddrr=\"" + fAddrR_ + "\"";
    ret = ret + " taddrl=\"" + tAddrL_ + "\"";
    ret = ret + " taddrr=\"" + tAddrR_ + "\"";

    return ret;
  }

  @Override
  public boolean isWalkable() {
    return type_ / 10 < LinkTypes.NONWALKABLE;
  }

  @Override
  public boolean isBikeable(BNode start) {
    if (start != null && isOneWay() && fNode_ != start) {
      return false;
    }
    return type_ / 10 < LinkTypes.NONWALKABLE;
  }

  public boolean isOneWay() {
    return type_ % 10 == 1;
  }

  public void makeOneWay() {
    if (type_ == LinkTypes.MAJOR_TWOWAY) {
      type_ = LinkTypes.MAJOR_ONEWAY;
    }
    if (type_ == LinkTypes.MINOR_TWOWAY) {
      type_ = LinkTypes.MINOR_ONEWAY;
    }
    if (type_ == LinkTypes.LOCAL_TWOWAY) {
      type_ = LinkTypes.LOCAL_ONEWAY;
    }
  }

  public void makeTwoWay() {
    if (type_ == LinkTypes.MAJOR_ONEWAY) {
      type_ = LinkTypes.MAJOR_TWOWAY;
    }
    if (type_ == LinkTypes.MINOR_ONEWAY) {
      type_ = LinkTypes.MINOR_TWOWAY;
    }
    if (type_ == LinkTypes.LOCAL_ONEWAY) {
      type_ = LinkTypes.LOCAL_TWOWAY;
    }
  }

  @Override
  public boolean isMajor() {
    return type_ / 10 == LinkTypes.MAJOR_TFARE || type_ / 10 == LinkTypes.MINOR_TFARE;
  } //major_; }

  @Override
  public boolean supportsTransitType(TransitPath.Type type, Scenario scen, BaseNetwork baseNet) {
    switch(type) {
      case BUS: return true;
      case METRO: case REGIONALRAIL: return false;
      case LIGHTRAIL:
        int transitProv = baseNet.getOverriddenTransitProv(this, scen);
        return transitProv == LinkTypes.TRPROV_BARRIER_SEPARATED_LRT || transitProv == LinkTypes.TRPROV_INTEGRATED_LRT;
    }
    return false;
  }

  public void setAddressRange(int fAddrL, int fAddrR, int tAddrL, int tAddrR) {
    fAddrL_ = fAddrL;
    fAddrR_ = fAddrR;
    tAddrL_ = tAddrL;
    tAddrR_ = tAddrR;
  }

  public void setZips(int zipL, int zipR) {
    zipL_ = zipL;
    zipR_ = zipR;
  }

  public boolean hasAddressRange() {
    return fAddrL_ > 0 || fAddrR_ > 0 || tAddrL_ > 0 || tAddrR_ > 0;
  }

  public int getFAddrL() {
    return fAddrL_;
  }

  public int getFAddrR() {
    return fAddrR_;
  }

  public int getTAddrL() {
    return tAddrL_;
  }

  public int getTAddrR() {
    return tAddrR_;
  }

  public int getZipL() {
    return zipL_;
  }

  public int getZipR() {
    return zipR_;
  }

  public int getZip(int num) {
    int minL = Math.min(fAddrL_, tAddrL_);
    int maxL = Math.max(fAddrL_, tAddrL_);
    int minR = Math.min(fAddrR_, tAddrR_);
    int maxR = Math.max(fAddrR_, tAddrR_);
    if (num % 2 == minL % 2 && num >= minL && num <= maxL) {
      return zipL_;
    }
    if (num % 2 == minR % 2 && num >= minR && num <= maxR) {
      return zipR_;
    }
    return 0;
  }

  public double getBikeFacTypeValue() {
    double val = 0;
    switch (type_ / 10) {
      case LinkTypes.MAJOR_TFARE:
        val = 1;
        break;
      case LinkTypes.MINOR_TFARE:
        val = .75;
        break;
      case LinkTypes.OTHER_STREET:
        val = .5;
        break;
      default:
        return 0;
    }
    if (bikePedProv_ == LinkTypes.BPPROV_BARRIER_SEPARATED_BIKE || bikePedProv_ == LinkTypes.BPPROV_BARRIER_SEPARATED_MULTIUSE) {
      return val - .5;
    }
    if (bikePedProv_ == LinkTypes.BPPROV_STRIPED_LANES_FULL || bikePedProv_ == LinkTypes.BPPROV_BIKEABLE_SHOULDER) {
      return val - .25;
    }

    return val;
  }

  @Override
  public String getDisplayText() {
    return "[" + scenario_.getName() + "] Street " + id_ + " ("+fNode_.getID() + " to " + tNode_.getID()+"): " + (getDisplayName().length() > 0 ? getDisplayName() : "[unnamed]") + " / L: " + fAddrL_ + "-" + tAddrL_ + ", " + zipL_ + " / R: " + fAddrR_ + "-" + tAddrR_ + ", " + zipR_ + " / len=" + lenFt_ + " / slope: " + upSlopeFW_ + " " + upSlopeBW_;
  }

  //public boolean isPartial() { return false; }
  public void fixAddressRange(BaseNetwork baseNet) {
    fixAddressNodeFromAdj(fNode_, fAddrR_, fAddrL_, baseNet);
    fixAddressNodeFromSelf(fNode_, fAddrR_, fAddrL_, baseNet);
    fixAddressNodeFromAdj(tNode_, tAddrR_, tAddrL_, baseNet);
    fixAddressNodeFromSelf(tNode_, tAddrR_, tAddrL_, baseNet);
  }

  private void fixAddressNodeFromAdj(BNode node, int addrR, int addrL, BaseNetwork baseNet) {
    String name = FPUtil.standardizeStreetName(getDisplayName());
    if (name.length() > 0) {
      Iterator<BLink> links = baseNet.incidentLinks(node).iterator();
      while (links.hasNext()) {
        BLink adjLink = links.next();
        String adjLinkName = FPUtil.standardizeStreetName(adjLink.getDisplayName());
        if (adjLink != this && adjLink.getClassType() == BLink.CLASS_STREET && adjLinkName.compareTo(name) == 0) {
          BLinkStreet st = (BLinkStreet) adjLink;
          int adjAddrR = (st.getFNode() == node ? st.getFAddrR() : st.getTAddrR());
          int adjAddrL = (st.getFNode() == node ? st.getFAddrL() : st.getTAddrL());
          if (addrR == 0) {
            System.out.println("addrR=0");
            if (node == fNode_) {
              fAddrR_ = st.getFNode() == node ? adjAddrL : adjAddrR;
            } else if (node == tNode_) {
              tAddrR_ = st.getFNode() == node ? adjAddrR : adjAddrL;
            }
          }
          if (addrL == 0) {
            System.out.println("addrL=0");
            if (node == fNode_) {
              fAddrL_ = st.getFNode() == node ? adjAddrR : adjAddrL;
            } else if (node == tNode_) {
              tAddrL_ = st.getFNode() == node ? adjAddrL : adjAddrR;
            }
          }
          break;
        }
      }
    }
  }

  private void fixAddressNodeFromSelf(BNode node, int addrR, int addrL, BaseNetwork baseNet) {
    if (addrR == 0 && addrL > 0) {
      if (node == fNode_) {
        fAddrR_ = addrL + 1;
      } else if (node == tNode_) {
        tAddrR_ = addrL + 1;
      }
    } else if (addrR > 0 && addrL == 0) {
      if (node == fNode_) {
        fAddrL_ = addrR + 1;
      } else if (node == tNode_) {
        tAddrL_ = addrR + 1;
      }
    }
  }

}

























